<?php namespace sacvoyage\http;

use sacvoyage\Object;

class JSONException extends \Exception {
	
}

function stdClass2Object($element) {
	if(is_object($element)) {
		$result=new Object();
		foreach($element as $name=>$value) {
			$result->$name=stdClass2Object($value);
		}
	} elseif(is_array($element)) {
		$result=array();
		foreach($element as $name=>$value) {
			$result[$name]=stdClass2Object($value);
		}
	} else {
		$result=$element;
	}	
	return $result;
}

function load_json($string) {
	$obj=json_decode($string);
	switch(json_last_error()) {
		case JSON_ERROR_NONE:
			$error=null; 
			break;
		case JSON_ERROR_DEPTH:
			$error='the maximum stack depth has been exceeded'; 
			break;
		case JSON_ERROR_STATE_MISMATCH:
			$error='invalid or malformed JSON'; 
			break;
		case JSON_ERROR_CTRL_CHAR:
			$error='control character error, possibly incorrectly encoded'; 
			break;
		case JSON_ERROR_SYNTAX:
			$error='syntax error'; 
			break;
		case JSON_ERROR_UTF8:
			$error='malformed UTF-8 characters, possibly incorrectly encoded'; 
			break;
	}
	if($error) {
		throw new JSONException("error decoding JSON: $error / $string");
	}
	return stdClass2Object($obj);
}

class JsonLoader implements \Iterator {

	public function __construct($file) {
		$data='';
		while(!feof($file)) {
			$data.=fread($file, 8192);			
		}
		
		$this->_documents=load_json($data);
		if(!is_enumerative_array($this->_documents)) {
			$this->_documents=array($this->_documents);
		}
	}

	public function rewind() {
		return rewind($this->_documents);
	}

	public function key() {
		return key($this->_documents);
	}

	public function current() {
		return current($this->_documents);
	}
	
	public function next() {
		next($this->_documents);
	}

	public function valid() {
		return valid($this->_documents);
	}

	private $_documents;
	
}



function norm_object($element) {
	if(is_array($element)) {
		ksort($element);
		$element=array_values($element);
	} elseif(is_object($element)) {
		$attrs=get_object_vars($element);
		ksort($attrs);
		foreach($attrs as $attr=>$value) {
			unset($element->$attr);
			$element->$attr=$value;
		}
	}
	return $element;
}

function save_json($element) {
	return json_encode(norm_object($element));
}

function ltrim_path($path) {
	return ltrim($path, " /\t\n\r\0\x0B");
}

function parse_path($path) {
	if(!$path) { // none results, of course, in (none, none)
		return array(null, null);		
	}	
	if(!is_string($path)) {
		throw new Exception('url must be string, '.gettype($path).' found');
	}
	$path=ltrim_path($path);
	$pos=strpos($path, '/');
	if($pos!==false) {
		return array(
			urldecode(substr($path, 0, $pos)),
			substr($path, $pos)
		);
	} else {
		return array(urldecode($path), null);
	}
	
	return;
}

function join_path() {
	$parts=array();
	foreach(func_get_args() as $arg) {
		$parts=trim($arg, "/ \t\n\r\0\x0B");	
	}
	return implode('/', $parts);
}


/**
 * version of sprintf for cases where named arguments are desired (php syntax)
 *
 * with sprintf: sprintf('second: %2$s ; first: %1$s', '1st', '2nd');
 *
 * with sprintfn: sprintfn('second: %second$s ; first: %first$s', array(
 *  'first' => '1st',
 *  'second'=> '2nd'
 * ));
 *
 * @param string $format sprintf format string, with any number of named arguments
 * @param array $args array of [ 'arg_name' => 'arg value', ... ] replacements to be made
 * @return string|false result of sprintf call, or bool false on error
 */
function sprintfn ($format, array $args = array()) {
    // map of argument names to their corresponding sprintf numeric argument value
    $arg_nums = array_slice(array_flip(array_keys(array(0 => 0) + $args)), 1);

    // find the next named argument. each search starts at the end of the previous replacement.
    for ($pos = 0; preg_match('/(?<=%)([a-zA-Z_]\w*)(?=\$)/', $format, $match, PREG_OFFSET_CAPTURE, $pos);) {
        $arg_pos = $match[0][1];
        $arg_len = strlen($match[0][0]);
        $arg_key = $match[1][0];

        // programmer did not supply a value for the named argument found in the format string
        if (! array_key_exists($arg_key, $arg_nums)) {
            user_error("sprintfn(): Missing argument '${arg_key}'", E_USER_WARNING);
            return false;
        }

        // replace the named argument with the corresponding numeric one
        $format = substr_replace($format, $replace = $arg_nums[$arg_key], $arg_pos, $arg_len);
        $pos = $arg_pos + strlen($replace); // skip to end of replacement for next iteration
    }

    return vsprintf($format, array_values($args));
}


?>